package roborally.factory;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import roborally.supporting.Position;

import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Immutable;

import exception.IllegalPositionException;

/**
 * A factory creating and managing all the position value objects. This class assures
 * when using this constructor, that no 2 positions with the same value are made.
 * 
 * @inv
 * 		| hasProperPositions()
 * 
 * @author Molderez Tom, 2de bach Ingenieurswetenschappen Computerwetenschappen-Elektrotechniek and Reniers Jorn, 2de bach Ingenieurswetenschappen Werktuigkunde-Computerwetenschappen
 * @version 2.0
 *
 */
public class PositionFactory {
		
	private PositionFactory() {
		
	}

	/**
	 * Returns this PositionFactory object.
	 */
	@Basic @Immutable
	public static PositionFactory get() {
		return positionFactory;
	}
	
	/**
	 * The positionFactory object used to create positions.
	 */
	private static final PositionFactory positionFactory = new PositionFactory();

	/**
	 * Returns a new position.
	 * 
	 * If the position with the given parameters already exits, a copy of the reference will
	 * be returned. Otherwise a new position will be created.
	 * When the arguments are not valid, an exception will be thrown.
	 * 
	 * @param x The x-coordinate of this position.
	 * @param y The y-coordinate of this position.
	 * @post This position will be in the list of existing positions.
	 * 		| (new this).getPositions().contains(new Position(x,y))
	 * @result Returns a position object with the given specifications.
	 * 		| let temp be new Position (x,y)
	 * 		| if this.getPositions().containsKey(temp)
	 * 		|	then result == this.getPositions().get(temp)
	 * 		| else
	 * 		| 	then result == temp
	 * @throws IllegalPositionException
	 * 		When the given parameters are not legal.
	 * 		| !Position.isValidX(x) || !Position.isValidY(y)
	 */
	public Position create(long x, long y) throws IllegalPositionException {
		Position temp = new Position(x,y);
		if(getPositions().containsKey(temp))
			return getPositions().get(temp);
		else {
			positions.put(temp, temp);
			return temp;
		}
	}
	
	/**
	 * Deletes the position so that it can be removed by the garbage collector.
	 * 
	 * @param position The position that must be removed.
	 */
	public void deletePosition(Position position) {
		positions.remove(position);
	}
	
	/**
	 * Returns all the positions that are created.
	 */
	@Basic
	public Map<Position, Position> getPositions() {
		return Collections.unmodifiableMap(positions);
	}
	
	/**
	 * Checks whether the Positions are valid.
	 * Therefore, all positions must be valid and every position may only exists once.
	 * 
	 * @return True if and only if every position satisfies its classInvariants and every position exists only once.
	 * 		| flag = true
	 * 		| for each position in {PositionFactory.getInstance().getPositions().keySet()}
	 * 		|	if !position.isValidX(position.getX()) || !position.isValidY(position.getY())
	 * 		|		then flag = false
	 * 		|	for each position2 in {PositionFactory.getInstance().getPositions().keySet()}
	 * 		|		if position2.equals(position) && position2 != position
	 * 		|			then flag = false
	 * 		| result == flag
	 */
	public static boolean hasProperPositions(){
		for(Position position: PositionFactory.get().getPositions().keySet()){
			if( !position.isValidX(position.getX()) || !position.isValidY(position.getY()) )
				return false;
			for(Position position2: PositionFactory.get().getPositions().keySet()){
				if (position2.equals(position) && position2 != position)
					return false;
			}
		}
		return true;
	}
	
	/**
	 * A map containing all the position-objects that are created. A synchronized map is needed for supporting threads.
	 */
	private static Map<Position, Position> positions = new ConcurrentHashMap<Position, Position>();
	
	/**
	 * Returns a textual specification of this EnergyAmountFactory.
	 * 
	 * @return A textual specification of this EnergyAmountFactory.
	 * 		| result == this.getClass().getSimpleName() + ": [Legal status: " + PositionFactory.hasProperPositions() + "], [Total: " + getPositions().size() + "], " +
	 *		|				"[Positions: " + getPositions().values().toString() + "]"
	 */
	@Override
	public String toString() {
		return this.getClass().getSimpleName() + ": [Legal status: " + PositionFactory.hasProperPositions() + "], [Total: " + getPositions().size() + "], " +
				"[Positions: " + getPositions().values().toString() + "]";
	}

}
